Namespace definitions
Global namespace containing types and functions accessible without any namespace prefix.
Global namespace containing types and functions accessible without any namespace prefix.
Types
An immutable signed integer type, supporting extremely large values (upwards of 100,000 decimal digits).
Literals of big_integer
type can be written like integers, but with the suffix L
, e.g. 123L
or 0x123L
. big_integer
s support the operators +
, -
, *
, /
and %
with typical behavior.
An array of bytes. This type is immutable.
A generic type for mutable ordered collections of elements. Subtype of iterable<T>
. Supports many standard operations such add insertion, removal, lookup and sorting.
A real number data type with high precision.
Not a complete equivalent of floating-point types, as there are a fixed maximum number of digits before the decimal point (131072
, or 2^17
digits) and after the decimal point (20
digits).
Examples:
123.456
.789
7e+33
decimal('123456789.98765')
An enum is a set of member constants who share a type.
The declaration enum example { A, B, C }
defines a type example
, with three member constants, example.A
, example.B
and example.C
.
Enum names and member constant names follow the same rules as all identifiers in Rell. However, by convention, enum names use snake_case
, and member constants use UPPER_SNAKE_CASE
.
Examples:
enum primary_color { RED, BLUE, GREEN }
enum error { TIMEOUT, MALFORMED_RESPONSE, NOT_FOUND, UNAUTHORIZED, UNKNOWN }
enum cardinal_direction { NORTH, EAST, SOUTH, WEST }
Generic Transfer Value (GTV) is a data type for the serialization and transfer of structured data, much like JSON.
GTV is used in Rell to encode operation and query arguments and results that are exchanged with clients. Unlike JSON, GTV has a stable byte serialization format and well-defined cryptographic hash, making it well-suited to this purpose. In addition, GTV supports byte arrays.
GTV supports the following types:
GTV Type | Closest Rell Equivalent |
---|---|
NULL | null |
BYTEARRAY | byte_array |
STRING | text |
INTEGER | integer |
DICT | map<text, gtv> |
ARRAY | list<gtv> |
BIGINTEGER | big_integer |
GTV does not support all Rell types, so not every value in Rell can be converted to GTV. For example, GTV has no support for non-integer numbers, and therefore the decimal
type is encoded in GTV as text.
Rell types can be encoded as GTV in two modes: compact and pretty, and the distinction between the two is a real semantic difference, and is not merely a difference in whitespace when converted to text. The two modes differ in the following ways:
Compact GTV encode struct values as a lists of attributes, while pretty GTV encode them as a dictionaries (thus struct member names are preserved).
Compact GTV encode named-field tuples as a lists of attributes, while pretty GTV encode them as a dictionaries (thus tuple field names are preserved). There is no difference between the two in encoding of unnamed-field tuples.
Examples of GTV:
>>> (x = 1, y = 'a', z = true).to_gtv()
[1,"a",1]
>>> (x = 1, y = 'a', z = false).to_gtv_pretty()
{"x":1,"y":"a","z":0}
>>> [1: 'a', 2: 'b', 3: 'c'].to_gtv()
[[1,"a"],[2,"b"],[3,"c"]]
>>> [1: 'a', 2: 'b', 3: 'c'].to_gtv_pretty()
[[1,"a"],[2,"b"],[3,"c"]]
>>> set([1, 2, 3, 4]).to_gtv()
[1,2,3,4]
>>> set([1, 2, 3, 4]).to_gtv_pretty()
[1,2,3,4]
>>> struct a { x: integer; y: decimal; };
>>> a(10, 10.1).to_gtv()
[10,"10.1"]
>>> a(10, 10.1).to_gtv_pretty()
{"x":10,"y":"10.1"}
Rell operations expect their arguments as compact-encoded GTV, whereas queries expect pretty-encoded GTV arguments, hence client applications are required to use those respective formats when making operation and query calls to Rell applications.
Generic Transfer Value (GTV) is a data type for the serialization and transfer of structured data, much like JSON.
GTV is used in Rell to encode operation and query arguments and results that are exchanged with clients. Unlike JSON, GTV has a stable byte serialization format and well-defined cryptographic hash, making it well-suited to this purpose. In addition, GTV supports byte arrays.
GTV supports the following types:
GTV Type | Closest Rell Equivalent |
---|---|
NULL | null |
BYTEARRAY | byte_array |
STRING | text |
INTEGER | integer |
DICT | map<text, gtv> |
ARRAY | list<gtv> |
BIGINTEGER | big_integer |
GTV does not support all Rell types, so not every value in Rell can be converted to GTV. For example, GTV has no support for non-integer numbers, and therefore the decimal
type is encoded in GTV as text.
Rell types can be encoded as GTV in two modes: compact and pretty, and the distinction between the two is a real semantic difference, and is not merely a difference in whitespace when converted to text. The two modes differ in the following ways:
Compact GTV encode struct values as a lists of attributes, while pretty GTV encode them as a dictionaries (thus struct member names are preserved).
Compact GTV encode named-field tuples as a lists of attributes, while pretty GTV encode them as a dictionaries (thus tuple field names are preserved). There is no difference between the two in encoding of unnamed-field tuples.
Examples of GTV:
>>> (x = 1, y = 'a', z = true).to_gtv()
[1,"a",1]
>>> (x = 1, y = 'a', z = false).to_gtv_pretty()
{"x":1,"y":"a","z":0}
>>> [1: 'a', 2: 'b', 3: 'c'].to_gtv()
[[1,"a"],[2,"b"],[3,"c"]]
>>> [1: 'a', 2: 'b', 3: 'c'].to_gtv_pretty()
[[1,"a"],[2,"b"],[3,"c"]]
>>> set([1, 2, 3, 4]).to_gtv()
[1,2,3,4]
>>> set([1, 2, 3, 4]).to_gtv_pretty()
[1,2,3,4]
>>> struct a { x: integer; y: decimal; };
>>> a(10, 10.1).to_gtv()
[10,"10.1"]
>>> a(10, 10.1).to_gtv_pretty()
{"x":10,"y":"10.1"}
Rell operations expect their arguments as compact-encoded GTV, whereas queries expect pretty-encoded GTV arguments, hence client applications are required to use those respective formats when making operation and query calls to Rell applications.
Represents a mutable array list. Subtype of collection<T>
.
A mutable map from keys of type K
to values of type V
, where K
is immutable, and V
may be either mutable or immutable. map<K,V> is a subtype of
iterable<(K,V)>`. It is implemented as a hash-map, with iteration order determined by the order in which the entries were added.
A mutable set of elements of type T
, where T
is immutable. Subtype of collection<T>
. Implemented as a hash-set, with iteration order determined by the order in which the elements were added.
Functions
Returns the absolute value of a big_integer value; i.e. the value itself if it's positive or its negation if it's negative.
Returns the absolute value of a decimal value; i.e. the value itself if it's positive or its negation if it's negative.
Returns the absolute value of a integer value; i.e. the value itself if it's positive or its negation if it's negative.
Asserts that two values are equal.
Asserts that the expected events has been emitted during last block
Asserts that a function fails to evaluate
Asserts that a function fails with an expected message
Asserts that the value is false
.
Asserts that the value is greater than or equal to the expected value
Asserts that the value is greater than or equal to the first value and less than or equal to the second value.
Asserts that the value is greater than or equal to the first value and less than the second value.
Asserts that the value is greater than the expected value
Asserts that the value is greater than the first value and less than or equal to the second value.
Asserts that the value is greater than the first value and less than the second value.
Asserts that the value is less than or equal to the expected value
Asserts that the value is less than the expected value
Asserts that two values are not equal.
Asserts that the value is not null
.
Asserts that the value is null
.
Asserts that the value is true
.
Compute an Ethererum public key from a signature and a hash.
Similar to Solidity's ecrecover()
, though differs in that:
This function takes
rec_id
, rather thanv
, whererec_id == v - 27
.The parameter order is different.
This function returns a 64-byte public key, not a 20-byte address. An address can be obtained by taking the last 20 bytes of the
keccak256()
digest of the returned public key, e.g.:
val address: byte_array = keccak256(eth_ecrecover(...)).sub(44);
The signature (consisting of the r
, s
and rec_id
components) will typically be obtained with a procedure equivalent to eth_sign(data_hash, privkey)
, where privkey
and pubkey
form a keypair (pubkey
being returned form this method).
The signature component rec_id
is an adjusted recovery identifier, equivalent to Ethereum's recovery identifier (usually denoted as v
) minus 27, i.e. rec_id == v - 27
.
The given 32-byte array data_hash
is typically a cryptographic hash obtained from a larger data structure using a hashing function such as hash()
, sha256()
or keccak256()
.
Example
The following is a Node.js script which uses ecrecover()
(the equivalent to crypto.eth_ecrecover()
) from the Ethereum Web3 library:
const Web3 = require('web3');
const web3 = new Web3();
var r = '0xcf722a47bcf1da61967ccc6405e31db4d37bce153255a6937e5cceb222caead0';
var s = '0xcf722a47bcf1da61967ccc6405e31db4d37bce153255a6937e5cceb222caead0';
var h = '0x53d7b11e61a8059aa4bc3248d24b2936436c9796dfe7f18e414c181004f79427';
var v = '0x1c';
var address = web3.eth.accounts.recover({'r':r,'s':s,'messageHash':h,'v':v});
console.log(address); // prints 0x5b0c087542D5C1E66Df0041e179c4201675B1614
An equivalent script in Rell is as follows:
val r = x'cf722a47bcf1da61967ccc6405e31db4d37bce153255a6937e5cceb222caead0';
val s = x'cf722a47bcf1da61967ccc6405e31db4d37bce153255a6937e5cceb222caead0';
val h = x'53d7b11e61a8059aa4bc3248d24b2936436c9796dfe7f18e414c181004f79427';
val v = 0x1c;
val pubkey = eth_ecrecover(r, s, v - 27, h);
val address = keccak256(pubkey).sub(12);
print(address); // prints 0x5b0c087542d5c1e66df0041e179c4201675b1614
Note that in the Rell script, v
is an integer, while in the Node.js script it is an 0x
-prefixed hexadecimal string.
Checks if a value is not null
or empty.
An argument can be:
Nullable type (
T?
) - checked fornull
.Collection (
list
,set
,map
) - checked for being empty (a nullable collection is also checked fornull
).
Special case: when used within a database at-expression, and the argument is also a database at-expression, it becomes a nested at-expression, which can use entities of the outer one. The call is translated into the SQL EXISTS
clause with a nested SELECT
.
Check if a given public key is a signer of the current transaction; i.e. if it's in the list of signers returned by op_context.get_signers()
.
Compute the Keccak256 digest (hash) of the given byte array.
Returns the greater of two big_integer values; i.e. a
if a > b
, or b
otherwise.
Returns the greater of two decimal values; i.e. a
if a > b
, or b
otherwise.
Returns the greater of two integer values; i.e. a
if a > b
, or b
otherwise.
Returns the lesser of two big_integer values; i.e. a
if a < b
, or b
otherwise.
Returns the lesser of two decimal values; i.e. a
if a < b
, or b
otherwise.
Returns the lesser of two integer values; i.e. a
if a < b
, or b
otherwise.
Asserts that a value is non-null.
Asserts that a list is non-null and non-empty.
Asserts that a map is non-null and non-empty.
Asserts that a set is non-null and non-empty.
Asserts that a value is non-null.
Asserts that a list is non-null and non-empty.
Asserts that a map is non-null and non-empty.
Asserts that a set is non-null and non-empty.
Compute the SHA-256 digest (hash) of the given byte array.
Calls a function and handles exceptions gracefully, returning null if an exception occurs.
Calls a function that doesn't return a value and handles exceptions gracefully.
Calls a function and handles exceptions gracefully, providing a fallback value if an exception occurs.
Verify a signature against a message and public key.
More precisely, verify that signature
was obtained with a procedure equivalent to get_signature(data_hash, privkey)
, where privkey
and pubkey
form a keypair.
Accepts valid public keys of size 33
or 65
bytes. Note that not all byte arrays of acceptable length constitute valid public keys.